import 'dart:convert';
import 'dart:io' as io;
import 'dart:io';
import 'dart:math';
import '../all_websocket.dart' as imp;

///通用 app 和 dosktop WebSocket 接口
class WebSocket implements imp.WebSocket {
  io.WebSocket _socket;

  imp.OnMessage _onMessage;

  @override
  set onMessage(imp.OnMessage value) {
    _onMessage = value;
  }

  imp.OnClose _onClose;

  @override
  void set onClose(value) {
    _onClose = value;
  }

  imp.OnError _onError;

  @override
  void set onError(value) {
    _onError = value;
  }

  @override
  Future<imp.WebSocket> connect(String url, {Iterable<String> protocols, Map<String, dynamic> headers}) async {
    try {
      url = url.replaceAll("wss", "https").replaceAll("ws", 'http');
      Random r = new Random();
      String key = base64.encode(List<int>.generate(8, (_) => r.nextInt(255)));
      HttpClient client = HttpClient(context: SecurityContext());
      client.badCertificateCallback = (X509Certificate cert, String host, int port) {
        print('SimpleWebSocket: Allow self-signed certificate => $host:$port. ');
        return true;
      };

      HttpClientRequest request = await client.getUrl(Uri.parse(url)); // form the correct url here
      request.headers.add('Connection', 'Upgrade');
      request.headers.add('Upgrade', 'websocket');
      request.headers.add('Sec-WebSocket-Version', '13'); // insert the correct version here
      request.headers.add('Sec-WebSocket-Key', key.toLowerCase());

      HttpClientResponse response = await request.close();
      Socket socket = await response.detachSocket();
      _socket = io.WebSocket.fromUpgradedSocket(
        socket,
        protocol: 'signaling',
        serverSide: false,
      );

      _socket.listen(_onDataListen, onDone: _onDoneListen, onError: _onErrorListen);
    } catch (e) {
      _onErrorListen(e);
    }
    return this;
  }

  void _onDataListen(data) {
    _onMessage?.call(data);
  }

  void _onErrorListen(e) {
    _onError?.call(e);
  }

  void _onDoneListen() {
    _onClose?.call();
  }

  @override
  void close() {
    _socket?.close();
    _socket = null;
  }

  @override
  void add(data) {
    try {
      _socket?.add(data);
    } catch (e) {}
  }
}
